//-------------------------------------------------------------------------------------------------
// Copyright (c) Bradford W. Mott and Flare Contributors
// North Carolina State University, Department of Computer Science
// The IntelliMedia Group
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//-------------------------------------------------------------------------------------------------

namespace Flare.Geom
{
    /// <summary>
    /// A point in the xy-plane represented by two numbers, (x, y), where x is the horizontal
    /// coordinate and y is the vertical coordinate.
    /// </summary>
    public struct Point
    {
        /// <summary>
        /// Horizontal coordinate of the point.
        /// </summary>
        public float x { get; set; }

        /// <summary>
        /// Vertical coordinate of the point.
        /// </summary>
        public float y { get; set; }

        /// <summary>
        /// Length of line segment from the origin to this point.
        /// </summary>
        public float length
        {
            get { return (float)global::System.Math.Sqrt(x * x + y * y); }
        }

        /// <summary>
        /// Create a new point using the given coordinates. By default a point at (0, 0) is created.
        /// </summary>
        /// <param name="x">The horizontal coordinate.</param>
        /// <param name="y">The vertical coordinate.</param>
        public Point(float x = 0.0f, float y = 0.0f) : this()
        {
            this.x = x;
            this.y = y;
        }

        /// <summary>
        /// Create a new point by copying the values of the given point.
        /// </summary>
        /// <param name="p">The point to copy.</param>
        public Point(Point p) : this()
        {
            this.x = p.x;
            this.y = p.y;
        }

        /// <summary>
        /// Create a new point by adding the given point to this point.
        /// </summary>
        /// <param name="p">The point to add to this point.</param>
        /// <returns>The new point.</returns>
        public Point Add(Point p)
        {
            return new Point(this.x + p.x, this.y + p.y);
        }

        /// <summary>
        /// Create a new point as a clone of this point.
        /// </summary>
        /// <returns>The new point.</returns>
        public Point Clone()
        {
            return new Point(this);
        }

        /// <summary>
        /// Copies coordinates from the given point into this point.
        /// </summary>
        /// <param name="p">The point to copy.</param>
        public void CopyFrom(Point p)
        {
            this.x = p.x;
            this.y = p.y;
        }

        /// <summary>
        /// Returns the distance between two points.
        /// </summary>
        /// <param name="p1">The first point.</param>
        /// <param name="p2">The second point.</param>
        /// <returns>The distance between the points.</returns>
        public static float Distance(Point p1, Point p2)
        {
            return (float)global::System.Math.Sqrt(global::System.Math.Pow(p1.x - p2.x, 2) +
                global::System.Math.Pow(p1.y - p2.y, 2));
        }

        /// <summary>
        /// Determines if the points are equal (i.e., they have the same x and y coordinates).
        /// </summary>
        /// <param name="p">The point to compare.</param>
        /// <returns>True if the two points are equal.</returns>
        public bool Equals(Point p)
        {
            return (this.x == p.x) && (this.y == p.y);
        }

        /// <summary>
        /// Offset the point by the given deltas.
        /// </summary>
        /// <param name="dx">Amount to offset along the x axis.</param>
        /// <param name="dy">Amount to offset along the y axis.</param>
        public void Offset(float dx, float dy)
        {
            this.x += dx;
            this.y += dy;
        }

        /// <summary>
        /// Set the point to the given coordinates.
        /// </summary>
        /// <param name="x">The new x coordinate.</param>
        /// <param name="y">The new y coordinate.</param>
        public void SetTo(float x, float y)
        {
            this.x = x;
            this.y = y;
        }

        /// <summary>
        /// Create a new point by subtracting the given point from this point.
        /// </summary>
        /// <param name="p">The point to subtract from this point.</param>
        /// <returns>The new point.</returns>
        public Point Subtract(Point p)
        {
            return new Point(this.x - p.x, this.y - p.y);
        }

        /// <summary>
        /// Returns a formatted string representation of the point.
        /// </summary>
        /// <returns>The point represented as a string.</returns>
        public override string ToString()
        {
            return string.Format("(x={0} y={1})", this.x, this.y);
        }
    }
}
